package com.jgp.form.service;

import com.jgp.common.security.UserAdapter;
import com.jgp.common.security.UserDetail;
import com.jgp.common.utils.JGPUtil;
import com.jgp.form.model.*;
import com.jgp.form.repository.VirtualFormAttrRepository;
import com.jgp.form.repository.VirtualFormAttrValueRepository;
import com.jgp.form.repository.VirtualFormRepository;
import com.jgp.form.vo.*;
import com.jgp.sys.query.*;
import com.jgp.sys.ui.GridResult;
import com.jgp.sys.ui.Pager;
import com.jgp.sys.utils.SpringContextUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.persistence.Table;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 项目   jgp-cloud-parent
 * 作者   loufei
 * 时间   2019-05-27
 */
@Log4j2
@Service
public class FormService {
    
    @Autowired
    private VirtualFormRepository formRepository;
    @Autowired
    private VirtualFormAttrRepository formAttrRepository;
    @Autowired
    private VirtualFormAttrValueRepository formAttrValueRepository;
    
    @Transactional
    public VirtualForm saveForm(VirtualForm form) {
        return formRepository.createOrUpdate(form);
    }
    
    @Transactional
    public void removeForm(String formId) {
        formRepository.delete(formId);
        
        QueryFilterList filters = new QueryFilterList();
        filters.addFilter("formId", Operator.eq, formId);
        
        formAttrRepository.delete(filters);
        formAttrValueRepository.delete(filters);
    }
    
    /**
     * 查询虚拟表单
     * id code 输入一个即可
     *
     * @param key id 或者 formCode
     * @return
     */
    public VirtualForm queryForm(String key) {
        return virtualForm(key);
    }
    
    public boolean exitFormByCode(String formCode) {
        return formRepository.existsDistinctByDelFlagIsFalseAndCode(formCode);
    }
    
    public boolean exitFormById(String id) {
        return formRepository.existsDistinctByDelFlagIsFalseAndId(id);
    }
    
    public List<VirtualForm> queryForm(String name, Pager pager) {
        QueryFilterList filters = new QueryFilterList();
        if (StringUtils.isNotBlank(name)) filters.addFilter("name", Operator.like, name);
        return formRepository.read(filters, pager);
    }
    
    @Transactional
    public void saveFormAttr(VirtualFormAttr formAttr) {
        Assert.notNull(formAttr.getFormId(), "表单ID不能为空!");
        
        String formId = formAttr.getFormId();
        VirtualForm form = formRepository.read(formId);
        formAttr.setFormCode(form.getCode());
        
        if (!dataSourceIsList(formAttr.getType())) {
            formAttr.setDropType(VirtualFormAttrDropType.NO);
        }
        formAttrRepository.createOrUpdate(formAttr);
    }
    
    public List<VirtualFormAttr> queryFormAttr(String label, String formId, Pager pager) {
        QueryFilterList filters = new QueryFilterList();
        filters.addFilter("formId", Operator.eq, formId);
        if (StringUtils.isNotBlank(label)) filters.addFilter("label", Operator.like, label);
        OrderList orders = new OrderList();
        orders.addOrder("sort", OrderDirection.ASC);
        return formAttrRepository.read(filters, orders, pager);
    }
    
    public Object formInfo(String key) {
        Map<String, Object> info = new HashMap<>();
        VirtualForm form = virtualForm(key);
        
        List<VirtualFormAttr> formAttrs = queryAllFormAttrs(form.getId(), null);
        
        info.put("form", form);
        info.put("attrs", formAttrs);
        
        return info;
    }
    
    private boolean dataSourceIsList(VirtualFormAttrType type) {
        boolean flag = false;
        switch (type) {
            case DROP_MULTI:
            case DROP_SINGLE:
            case DROP_TREE:
            case LIST_MULTI:
            case LIST_SINGLE:
                flag = true;
        }
        return flag;
    }
    
    @Transactional
    public void configFormAttr(VirtualFormAttrConfig config) {
        Map<String, Integer> sortMap = config.getSortMap();
        Map<String, Boolean> inGrids = config.getInGridsMap();
        Set<String> sortKeys = sortMap.keySet();
        Set<String> inGridKeys = inGrids.keySet();
        List<VirtualFormAttr> attrs = formAttrRepository.readAll();
        attrs.forEach(attr -> {
            if (sortKeys.contains(attr.getId())) {
                attr.setSort(sortMap.get(attr.getId()));
            }
            
            if (inGridKeys.contains(attr.getId())) {
                attr.setInGrid(inGrids.get(attr.getId()));
            }
        });
        formAttrRepository.updateInBatch(attrs);
    }
    
    public VirtualFormAttr queryFormAttrById(String attrId) {
        return formAttrRepository.read(attrId);
    }
    
    @Transactional
    public void removeFormAttr(String attrId) {
        formAttrRepository.deletePersist(attrId);
        QueryFilterList filters = new QueryFilterList();
        filters.addFilter("attrId", Operator.eq, attrId);
        formAttrValueRepository.deletePersist(filters);
    }
    
    @Transactional
    public void createFormValue(VirtualFormData formData) {
        List<VirtualFormAttrValue> updateAttrValues = new ArrayList<>();
        List<VirtualFormAttrValue> attrValues = new ArrayList<>();
        String formId = formData.getFormId(), formCode = formData.getFormCode(), bizId = formData.getBizId();
        Map<String, Object> data = formData.getData();
        String key = StringUtils.isNotBlank(formId) ? formId : formCode;
        VirtualForm form = queryForm(key);
        if (Objects.isNull(form)) return;
        List<VirtualFormAttr> attrs = formAttrRepository.findAllByFormId(form.getId());
        List<VirtualFormAttrValue> persistAttrValues = formAttrValueRepository.findByBizId(bizId);
        Map<String, String> persistIdMap = persistAttrValues.stream().collect(
                Collectors.toMap(VirtualFormAttrValue::getAttrName, VirtualFormAttrValue::getId));
        attrs.forEach(formAttr -> {
            VirtualFormAttrValue attrValue = new VirtualFormAttrValue();
            attrValue.setFormId(form.getId());
            attrValue.setFormCode(form.getCode());
            attrValue.setAttrId(formAttr.getId());
            attrValue.setAttrName(formAttr.getName());
            attrValue.setBizId(bizId);
            Object value = data.get(formAttr.getName());
            attrValue.setValue(value + "");
            String id = persistIdMap.get(formAttr.getName());
            if (StringUtils.isNotBlank(id)) {
                attrValue.setId(id);
                updateAttrValues.add(attrValue);
            } else {
                attrValues.add(attrValue);
            }
        });
        if (attrValues.size() > 0) {
            createFormValue(attrValues);
        }
        if (updateAttrValues.size() > 0) {
            updateFormValue(updateAttrValues);
        }
    }
    
    @Transactional
    public void removeFormValue(String bizId) {
        QueryFilterList filters = new QueryFilterList();
        filters.addFilter("bizId", Operator.eq, bizId);
        formAttrValueRepository.deletePersist(filters);
    }
    
    @Transactional
    public void createFormValue(List<VirtualFormAttrValue> attrValues) {
        formAttrValueRepository.createInBatch(attrValues);
    }
    
    @Transactional
    public void updateFormValue(List<VirtualFormAttrValue> attrValues) {
        formAttrValueRepository.updateInBatch(attrValues);
    }
    
    
    public VirtualFormResponse queryFormValue(VirtualFormValueQuery query, Join join) {
        String formCode = query.getFormCode();
        
        String clientId = getClientId();
        List<VirtualFormAttr> attrs;
        if (StringUtils.isNotBlank(clientId)) {
            attrs = formAttrRepository.findAllByClientIdAndFormCode(clientId, formCode);
        } else {
            attrs = formAttrRepository.findAllByFormCode(formCode);
        }
        attrs = attrs.stream().map(formAttr -> {
            if (Objects.isNull(formAttr.getSort())) {
                formAttr.setSort(0);
            }
            return formAttr;
        }).sorted(Comparator.comparing(VirtualFormAttr::getSort).reversed()).collect(Collectors.toList());
        String tempTable = "new_table";
        String tempTableName = "`new_table`";
        StringBuilder tableSql = new StringBuilder(" (SELECT biz_id ");
        for (VirtualFormAttr attr : attrs) {
            tableSql.append(",");
            tableSql.append("MAX(CASE attr_name WHEN '");
            tableSql.append(attr.getName());
            tableSql.append("' THEN `value` ELSE NULL END) AS ");
            tableSql.append("`");
            tableSql.append(tempTable);
            tableSql.append(".");
            tableSql.append(attr.getName());
            tableSql.append("` ");
        }
        tableSql.append(" FROM sys_virtual_form_attr_value WHERE form_code='");
        tableSql.append(formCode);
        tableSql.append("' ");
        if (StringUtils.isNotBlank(clientId)) {
            tableSql.append(" AND client_id='");
            tableSql.append(clientId);
            tableSql.append("' ");
        }
        tableSql.append(" GROUP BY biz_id) AS ");
        tableSql.append(tempTableName);
        String joinTableName = null, joinTable = null;
        
        if (Objects.nonNull(join)) {
            joinTable = join.getJoinClass().getAnnotation(Table.class).name();
            joinTableName = "`" + joinTable + "`";
        }
        
        if (Objects.nonNull(join)) {
            StringBuffer joinSql = new StringBuffer(join.getJoinDirection().getJoin());
            joinSql.append(joinTableName);
            joinSql.append(" AS ");
            joinSql.append(joinTableName);
            joinSql.append(" ON ");
            joinSql.append(tempTableName);
            joinSql.append(".`");
            joinSql.append(JGPUtil.camel2Underline(join.getLeftField()).toLowerCase());
            joinSql.append("` = ");
            joinSql.append(joinTableName);
            joinSql.append(".`");
            joinSql.append(JGPUtil.camel2Underline(join.getJoinField()).toLowerCase());
            joinSql.append("` ");
            tableSql.append(joinSql);
        }
        tableSql.append(" WHERE 1=1 ");
        
        if (Objects.nonNull(query.getFilters()) && query.getFilters().size() > 0) {
            QueryFilterList filters = new QueryFilterList();
            filters.addFilters(query.getFilters().stream().filter(q -> !q.getName().contains(".")).collect(Collectors.toList()));
            tableSql.append(whereSql(tempTable, filters, true));
        }
        if (Objects.nonNull(join) && Objects.nonNull(join.getJoinFilters()) && join.getJoinFilters().size() > 0) {
            QueryFilterList joinFilters = join.getJoinFilters();
            tableSql.append(whereSql(joinTable, joinFilters, false));
        }
        
        String countSql = "SELECT COUNT(*) FROM " + tableSql;
        
        String sql;
        if ((Objects.nonNull(query.getOrders()) && query.getOrders().size() > 0) || (Objects.nonNull(join) && Objects
                .nonNull(join.getJoinOrders()) && join.getJoinOrders().size() > 0)) {
            tableSql.append(" ORDER BY ");
            tableSql.append(orderSql(tempTableName, query.getOrders()));
            tableSql.append(orderSql(joinTableName, join.getJoinOrders()));
            sql = "SELECT " + tempTableName + ".*," + joinTableName + ".* FROM " + tableSql.substring(0, tableSql.lastIndexOf(","));
        } else {
            sql = "SELECT * FROM " + tableSql.toString();
        }
        
        List<Map<String, Object>> values = formAttrValueRepository.readNativeSqlToMapWithOutTransformName(countSql, sql, query.getPager());
        
        List<Map<String, Object>> result = values.stream().map(map -> {
            String bizId = map.get("biz_id").toString();
            Iterator<String> iterator = map.keySet().iterator();
            VirtualFormResponseData virtualFormResponseData = new VirtualFormResponseData();
            Map<String, Object> other = new HashMap<>();
            while (iterator.hasNext()) {
                String key = iterator.next();
                Object v = map.get(key);
                if (key.contains(".")) {
                    String k = key.split("\\.")[1];
                    virtualFormResponseData.put(k, v);
                } else {
                    other.put(JGPUtil.underline2Camel(key, true), v);
                }
            }
            virtualFormResponseData.put("bizId", bizId);
            other.put("bizId", bizId);
            Map<String, Object> m = new HashMap<>();
            m.put("virtualFormResponseData", virtualFormResponseData);
            if (Objects.nonNull(join)) {
                m.put(JGPUtil.lowerFirstCase(join.getJoinClass().getSimpleName()), other);
            }
            return m;
        }).collect(Collectors.toList());
        List<GridResult.Category> categories = new ArrayList<>();
        for (VirtualFormAttr attr : attrs) {
            if (query.getAllAttr() || attr.getInGrid()) {
                categories.add(createCategory(attr));
            }
        }
        
        return new VirtualFormResponse(query.getPager(), result, categories);
    }
    
    private GridResult.Category createCategory(VirtualFormAttr attr) {
        String name = "virtualFormResponseData." + attr.getName();
        GridResult.Category category = null;
        if (dataSourceIsList(attr.getType())) {
            if (VirtualFormAttrType.DROP_TREE.equals(attr.getType())) {
                String model = attr.getModel();
                try {
                    Class clazz = Class.forName(model);
                    category = new GridResult.Category(attr.getLabel(), name, clazz, attr.getMapperName());
                } catch (ClassNotFoundException e) {
                    category = new GridResult.Category(attr.getLabel(), name);
                }
            } else if (VirtualFormAttrDropType.ACTIVE_KEY.equals(attr.getDropType())) {
                category = new GridResult.Category(attr.getLabel(), name, attr.getDataSource());
            } else if (VirtualFormAttrDropType.LOCAL.equals(attr.getDropType()) && attr.getDataSource().contains(":")) {
                String dataSource = attr.getDataSource();
                String[] arr = dataSource.split(":");
                try {
                    Class modelClass = Class.forName(arr[0]);
                    category = new GridResult.Category(attr.getLabel(), name, modelClass, arr[1]);
                } catch (ClassNotFoundException e) {
                    log.error("未找到实体:" + dataSource, e);
                }
            } else {
                category = new GridResult.Category(attr.getLabel(), name);
            }
        } else {
            category = new GridResult.Category(attr.getLabel(), name);
        }
        return category;
    }
    
    private String whereSql(String tableAlias, QueryFilterList filters, Boolean transformName) {
        StringBuilder sql = new StringBuilder();
        filters.forEach(filter -> {
            sql.append(" AND ");
            sql.append(filterSql(tableAlias, filter, transformName));
        });
        return sql.toString();
    }
    
    private String filterSql(String tableAlias, QueryFilter filter, Boolean transformName) {
        StringBuilder sql = new StringBuilder();
        if (transformName) {
            sql.append(" `" + tableAlias + "." + filter.getName() + "` ");
        } else {
            sql.append(" `" + tableAlias + "`.`" + JGPUtil.camel2Underline(filter.getName()).toLowerCase() + "` ");
        }
        sql.append(filter.getOperator().toSql(filter.getValue()));
        return sql.toString();
    }
    
    private String orderSql(String tableAlias, OrderList orders) {
        if (Objects.isNull(orders) || orders.size() == 0) {
            return "";
        }
        StringBuilder sql = new StringBuilder(" ");
        orders.forEach(order -> {
            sql.append(tableAlias + ".`" + JGPUtil.camel2Underline(order.getField()).toLowerCase() + "` " + order.getDirection().name());
            sql.append(",");
        });
        return sql.toString();
    }
    
    public VirtualFormResponseData formValue(String bizId) {
        QueryFilterList filters = new QueryFilterList();
        filters.addFilter("bizId",Operator.eq, bizId);
        List<VirtualFormAttrValue> attrValues = formAttrValueRepository.read(filters);
        VirtualFormResponseData data = new VirtualFormResponseData();
        attrValues.forEach(virtualFormAttrValue -> {
            data.put(virtualFormAttrValue.getAttrName(), virtualFormAttrValue.getValue());
        });
        return data;
    }
    
    public VirtualForm virtualForm(String key) {
        String clientId = getClientId();
        if (StringUtils.isNotBlank(clientId)) {
            return formRepository.findByClientIdAndIdOrClientIdAndCode(clientId, key, clientId, key);
        } else {
            return formRepository.findByIdOrCode(key, key);
        }
    }
    
    public List<VirtualFormAttr> queryAllFormAttrs(String formId, String formCode) {
        
        QueryFilterList filters = new QueryFilterList();
        
        if (StringUtils.isNotBlank(formId)) {
            filters.addFilter("formId", Operator.eq, formId);
        } else if (StringUtils.isNotBlank(formCode)) filters.addFilter("formCode", Operator.eq, formCode);
        
        OrderList orders = new OrderList();
        orders.addOrder("sort", OrderDirection.ASC);
        
        return formAttrRepository.read(filters, orders);
    }
    
    private String getClientId() {
        UserAdapter userAdapter = SpringContextUtil.getBean(UserAdapter.class);
        String clientId = null;
        if (Objects.nonNull(userAdapter)) {
            UserDetail userDetail = userAdapter.currentUser();
            if (Objects.nonNull(userDetail)) {
                clientId = userDetail.getClientId();
            }
        }
        return clientId;
    }
    
}
